/*
* Copyright 2009-2010 MBTE Sweden AB.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mbte.groovypp.compiler.transformers;
import groovy.lang.TypePolicy;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.classgen.BytecodeHelper;
import org.codehaus.groovy.syntax.Token;
import org.codehaus.groovy.syntax.Types;
import org.mbte.groovypp.compiler.CompilerTransformer;
import org.mbte.groovypp.compiler.PresentationUtil;
import org.mbte.groovypp.compiler.TypeUtil;
import org.mbte.groovypp.compiler.bytecode.*;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
public class BinaryExpressionTransformer extends ExprTransformer<BinaryExpression> {
private static final Token INTDIV = Token.newSymbol(Types.INTDIV, -1, -1);
private static final Token DIVIDE = Token.newSymbol(Types.DIVIDE, -1, -1);
private static final Token RIGHT_SHIFT_UNSIGNED = Token.newSymbol(Types.RIGHT_SHIFT_UNSIGNED, -1, -1);
private static final Token RIGHT_SHIFT = Token.newSymbol(Types.RIGHT_SHIFT, -1, -1);
private static final Token LEFT_SHIFT = Token.newSymbol(Types.LEFT_SHIFT, -1, -1);
private static final Token POWER = Token.newSymbol(Types.POWER, -1, -1);
private static final Token MOD = Token.newSymbol(Types.MOD, -1, -1);
private static final Token MULTIPLY = Token.newSymbol(Types.MULTIPLY, -1, -1);
private static final Token BITWISE_XOR = Token.newSymbol(Types.BITWISE_XOR, -1, -1);
private static final Token BITWISE_OR = Token.newSymbol(Types.BITWISE_OR, -1, -1);
private static final Token BITWISE_AND = Token.newSymbol(Types.BITWISE_AND, -1, -1);
private static final Token MINUS = Token.newSymbol(Types.MINUS, -1, -1);
private static final Token PLUS = Token.newSymbol(Types.PLUS, -1, -1);
public Expression transform(BinaryExpression exp, CompilerTransformer compiler) {
switch (exp.getOperation().getType()) {
case Types.COMPARE_EQUAL:
case Types.COMPARE_NOT_EQUAL:
case Types.LOGICAL_AND:
case Types.LOGICAL_OR:
case Types.KEYWORD_INSTANCEOF:
case Types.COMPARE_IDENTICAL: // ===
case Types.COMPARE_NOT_IDENTICAL: // ===
case Types.COMPARE_GREATER_THAN:
case Types.COMPARE_GREATER_THAN_EQUAL:
case Types.COMPARE_LESS_THAN:
case Types.COMPARE_LESS_THAN_EQUAL:
return new Logical(exp, compiler);
case Types.EQUAL:
return evaluateAssign(exp, compiler);
case Types.LEFT_SQUARE_BRACKET:
return evaluateArraySubscript(exp, compiler);
case Types.MULTIPLY:
case Types.DIVIDE:
case Types.MINUS:
case Types.PLUS:
case Types.BITWISE_XOR:
case Types.BITWISE_AND:
case Types.INTDIV:
case Types.LEFT_SHIFT:
case Types.RIGHT_SHIFT:
case Types.RIGHT_SHIFT_UNSIGNED:
case Types.MOD:
case Types.BITWISE_OR:
case Types.POWER:
return evaluateMathOperation(exp, compiler);
case Types.COMPARE_TO:
return evaluateCompareTo(exp, compiler);
case Types.PLUS_EQUAL:
return evaluateMathOperationAssign(exp, PLUS, compiler);
case Types.MINUS_EQUAL:
return evaluateMathOperationAssign(exp, MINUS, compiler);
case Types.BITWISE_AND_EQUAL:
return evaluateMathOperationAssign(exp, BITWISE_AND, compiler);
case Types.BITWISE_OR_EQUAL:
return evaluateMathOperationAssign(exp, BITWISE_OR, compiler);
case Types.BITWISE_XOR_EQUAL:
return evaluateMathOperationAssign(exp, BITWISE_XOR, compiler);
case Types.MULTIPLY_EQUAL:
return evaluateMathOperationAssign(exp, MULTIPLY, compiler);
case Types.MOD_EQUAL:
return evaluateMathOperationAssign(exp, MOD, compiler);
case Types.POWER_EQUAL:
return evaluateMathOperationAssign(exp, POWER, compiler);
case Types.LEFT_SHIFT_EQUAL:
return evaluateMathOperationAssign(exp, LEFT_SHIFT, compiler);
case Types.RIGHT_SHIFT_EQUAL:
return evaluateMathOperationAssign(exp, RIGHT_SHIFT, compiler);
case Types.RIGHT_SHIFT_UNSIGNED_EQUAL:
return evaluateMathOperationAssign(exp, RIGHT_SHIFT_UNSIGNED, compiler);
case Types.DIVIDE_EQUAL:
return evaluateMathOperationAssign(exp, DIVIDE, compiler);
case Types.INTDIV_EQUAL:
return evaluateMathOperationAssign(exp, INTDIV, compiler);
case Types.FIND_REGEX:
return evaluateFindRegexp(exp, compiler);
case Types.MATCH_REGEX:
return evaluateMatchRegexp(exp, compiler);
case Types.KEYWORD_IN: {
final BytecodeExpr left = (BytecodeExpr) compiler.transform(exp.getLeftExpression());
final BytecodeExpr right = (BytecodeExpr) compiler.transform(exp.getRightExpression());
return callMethod(exp, "isCase", compiler, right, left);
}
default: {
compiler.addError("Operation: " + exp.getOperation() + " not supported", exp);
return null;
}
}
}
private Expression evaluateCompareTo(BinaryExpression be, CompilerTransformer compiler) {
final Operands operands = new Operands(be, compiler);
return new BytecodeExpr(be, ClassHelper.Integer_TYPE) {
protected void compile(MethodVisitor mv) {
operands.getLeft().visit(mv);
if (ClassHelper.isPrimitiveType(operands.getLeft().getType()))
box(operands.getLeft().getType(), mv);
operands.getRight().visit(mv);
if (ClassHelper.isPrimitiveType(operands.getRight().getType()))
box(operands.getRight().getType(), mv);
mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/ScriptBytecodeAdapter", "compareTo", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Integer;");
}
};
}
public BytecodeExpr transformLogical(BinaryExpression exp, CompilerTransformer compiler, Label label, boolean onTrue) {
final int op = exp.getOperation().getType();
switch (op) {
case Types.LOGICAL_AND:
return evaluateLogicalAnd(exp, compiler, label, onTrue);
case Types.LOGICAL_OR:
return evaluateLogicalOr(exp, compiler, label, onTrue);
case Types.KEYWORD_INSTANCEOF:
return evaluateInstanceof(exp, compiler, label, onTrue);
case Types.COMPARE_NOT_EQUAL:
case Types.COMPARE_EQUAL:
case Types.COMPARE_IDENTICAL: // ===
case Types.COMPARE_NOT_IDENTICAL: // !==
case Types.COMPARE_GREATER_THAN:
case Types.COMPARE_GREATER_THAN_EQUAL:
case Types.COMPARE_LESS_THAN:
case Types.COMPARE_LESS_THAN_EQUAL:
return evaluateCompare(exp, compiler, label, onTrue, op);
case Types.COMPARE_TO:
throw new UnsupportedOperationException();
default: {
return super.transformLogical(exp, compiler, label, onTrue);
}
}
}
private BytecodeExpr evaluateInstanceof(BinaryExpression be, CompilerTransformer compiler, final Label label, final boolean onTrue) {
final BytecodeExpr l = (BytecodeExpr) compiler.transform(be.getLeftExpression());
final ClassNode type = be.getRightExpression().getType();
return new BytecodeExpr(be, ClassHelper.boolean_TYPE) {
protected void compile(MethodVisitor mv) {
l.visit(mv);
box(l.getType(), mv);
mv.visitTypeInsn(INSTANCEOF, BytecodeHelper.getClassInternalName(type));
mv.visitJumpInsn(onTrue ? IFNE : IFEQ, label);
}
};
}
private BytecodeExpr unboxReference(BinaryExpression parent, BytecodeExpr left, CompilerTransformer compiler) {
MethodNode unboxing = TypeUtil.getReferenceUnboxingMethod(left.getType());
if (unboxing != null) {
left = ResolvedMethodBytecodeExpr.create(parent, unboxing, left, new ArgumentListExpression(), compiler);
}
return left;
}
private String getMethod(int token) {
switch (token) {
case (Types.MULTIPLY): return "multiply";
case (Types.DIVIDE): return "div";
case (Types.MINUS): return "minus";
case (Types.PLUS): return "plus";
case (Types.BITWISE_XOR): return "xor";
case (Types.BITWISE_AND): return "and";
case (Types.BITWISE_OR): return "or";
case (Types.INTDIV): return "intdiv";
case (Types.LEFT_SHIFT): return "leftShift";
case (Types.RIGHT_SHIFT): return "rightShift";
case (Types.RIGHT_SHIFT_UNSIGNED): return "rightShiftUnsigned";
case (Types.MOD): return "mod";
case (Types.POWER): return "power";
default:
throw new IllegalStateException("Wrong token type");
}
}
private Expression evaluateMathOperation(final BinaryExpression be, final CompilerTransformer compiler) {
final Operands operands = new Operands(be, compiler);
final BytecodeExpr l = operands.getLeft();
final BytecodeExpr r = operands.getRight();
final int tokenType = be.getOperation().getType();
if (TypeUtil.isNumericalType(l.getType()) && TypeUtil.isNumericalType(r.getType())) {
if (tokenType == Types.POWER)
return callMethod(be, getMethod(tokenType), compiler, l, r);
ClassNode mathType0 = TypeUtil.getMathType(l.getType(), r.getType());
if (tokenType == Types.DIVIDE
&& (
/*mathType0.equals(ClassHelper.int_TYPE) ||
mathType0.equals(ClassHelper.long_TYPE) ||*/
mathType0.equals(ClassHelper.BigInteger_TYPE)))
mathType0 = ClassHelper.BigDecimal_TYPE;
final ClassNode mathType = mathType0;
if (mathType == ClassHelper.BigDecimal_TYPE || mathType == ClassHelper.BigInteger_TYPE || mathType == TypeUtil.Number_TYPE)
return compiler.cast(callMethod(be, getMethod(tokenType), compiler, l, r), mathType);
if (mathType != ClassHelper.int_TYPE && mathType != ClassHelper.long_TYPE) {
switch (tokenType) {
case Types.BITWISE_XOR:
case Types.BITWISE_AND:
case Types.INTDIV:
case Types.LEFT_SHIFT:
case Types.RIGHT_SHIFT:
case Types.RIGHT_SHIFT_UNSIGNED:
case Types.BITWISE_OR:
return compiler.cast(callMethod(be, getMethod(tokenType), compiler, l, r), mathType);
}
}
return new BytecodeExpr(be, mathType) {
protected void compile(MethodVisitor mv) {
l.visit(mv);
box(l.getType(), mv);
cast(TypeUtil.wrapSafely(l.getType()), TypeUtil.wrapSafely(mathType), mv);
if (ClassHelper.isPrimitiveType(mathType))
unbox(mathType, mv);
r.visit(mv);
box(r.getType(), mv);
cast(TypeUtil.wrapSafely(r.getType()), TypeUtil.wrapSafely(mathType), mv);
if (ClassHelper.isPrimitiveType(mathType))
unbox(mathType, mv);
compiler.mathOp(mathType, be.getOperation(), be);
}
};
} else if (l.getType() == ClassHelper.STRING_TYPE && tokenType == Types.PLUS) {
Expression stringBuilder = new ConstructorCallExpression(TypeUtil.STRING_BUILDER,
new ArgumentListExpression());
stringBuilder = stringBuilderAppend(l, r, stringBuilder);
Expression res = new MethodCallExpression(stringBuilder, "toString", new ArgumentListExpression());
return compiler.transform(res); // NB: some of the terms are already transformed.
} else {
return callMethod(be, getMethod(tokenType), compiler, l, r);
}
}
private Expression stringBuilderAppend(Expression l, Expression r, Expression stringBuilder) {
if (l instanceof BinaryExpression && ((BinaryExpression) l).getOperation().getType() == Types.PLUS) {
BinaryExpression be = (BinaryExpression) l;
stringBuilder = stringBuilderAppend(be.getLeftExpression(), be.getRightExpression(), stringBuilder);
} else {
stringBuilder = new MethodCallExpression(stringBuilder, "append", new ArgumentListExpression(l));
}
return new MethodCallExpression(stringBuilder, "append", new ArgumentListExpression(r));
}
private Expression callMethod(BinaryExpression be, String method, CompilerTransformer compiler, BytecodeExpr l, BytecodeExpr r) {
ConstantExpression methodExpression = new ConstantExpression(method);
methodExpression.setLineNumber(be.getOperation().getStartLine());
methodExpression.setColumnNumber(be.getOperation().getStartColumn());
final MethodCallExpression mce = new MethodCallExpression(l, methodExpression, new ArgumentListExpression(r));
mce.setSourcePosition(be);
return compiler.transform(mce);
}
private Expression evaluateAssign(BinaryExpression be, CompilerTransformer compiler) {
BytecodeExpr left = (BytecodeExpr) compiler.transform(be.getLeftExpression());
if (!(left instanceof ResolvedLeftExpr)) {
compiler.addError("Assignment operator is applicable only to variable or property or array element", be);
return null;
}
BytecodeExpr right = (BytecodeExpr) compiler.transform(be.getRightExpression());
MethodNode boxing = TypeUtil.getReferenceBoxingMethod(left.getType(), right.getType());
if (boxing != null && !TypeUtil.isDirectlyAssignableFrom(left.getType(), right.getType())) {
return ResolvedMethodBytecodeExpr.create(be, boxing, left, new ArgumentListExpression(right), compiler);
}
return ((ResolvedLeftExpr) left).createAssign(be, right, compiler);
}
private Expression evaluateMathOperationAssign(BinaryExpression be, Token op, CompilerTransformer compiler) {
Expression left = compiler.transform(be.getLeftExpression());
if (!(left instanceof ResolvedLeftExpr)) {
compiler.addError("Assignment operator is applicable only to variable or property or array element", be);
return null;
}
final BytecodeExpr right = (BytecodeExpr) compiler.transform(be.getRightExpression());
MethodNode lunboxing = TypeUtil.getReferenceUnboxingMethod(left.getType());
MethodNode rboxing = TypeUtil.getReferenceBoxingMethod(left.getType(), right.getType());
if (lunboxing != null && rboxing != null) {
final ResolvedMethodBytecodeExpr oldValue = ResolvedMethodBytecodeExpr.create(be, lunboxing,
(BytecodeExpr) left, new ArgumentListExpression(), compiler);
final BinaryExpression binary = new BinaryExpression(oldValue, op, right);
binary.setSourcePosition(be);
final Expression opApplied = evaluateMathOperation(binary, compiler);
return ResolvedMethodBytecodeExpr.create(be, rboxing,
(BytecodeExpr) left, new ArgumentListExpression(new Expression[]{opApplied}), compiler);
}
return ((ResolvedLeftExpr) left).createBinopAssign(be, op, right, compiler);
}
private Expression evaluateArraySubscript(final BinaryExpression bin, CompilerTransformer compiler) {
final BytecodeExpr object = (BytecodeExpr) compiler.transformToGround(bin.getLeftExpression());
final BytecodeExpr indexExp = (BytecodeExpr) compiler.transform(bin.getRightExpression());
if (object.getType().isArray() && TypeUtil.isAssignableFrom(int_TYPE, indexExp.getType()))
return new ResolvedArrayBytecodeExpr(bin, object, indexExp, compiler);
else {
MethodNode getter = compiler.findMethod(object.getType(), "getAt", new ClassNode[]{indexExp.getType()}, false);
if (getter == null) {
MethodNode unboxing = TypeUtil.getReferenceUnboxingMethod(object.getType());
if (unboxing != null) {
ClassNode t = TypeUtil.getSubstitutedType(unboxing.getReturnType(), unboxing.getDeclaringClass(), object.getType());
getter = compiler.findMethod(t, "getAt", new ClassNode[]{indexExp.getType()}, false);
if (getter != null) {
BytecodeExpr object1 = ResolvedMethodBytecodeExpr.create(bin, unboxing, object,
new ArgumentListExpression(), compiler);
return new ResolvedArrayLikeBytecodeExpr(bin, object1, indexExp, getter, compiler);
}
}
} else {
return new ResolvedArrayLikeBytecodeExpr(bin, object, indexExp, getter, compiler);
}
if (indexExp instanceof ListExpressionTransformer.UntransformedListExpr) {
MethodCallExpression mce = new MethodCallExpression(object, "getAt", new ArgumentListExpression(((ListExpressionTransformer.UntransformedListExpr) indexExp).exp.getExpressions()));
mce.setSourcePosition(bin);
return compiler.transform(mce);
}
if (compiler.policy == TypePolicy.STATIC) {
compiler.addError("Cannot find method 'getAt' for type: " + PresentationUtil.getText(object.getType()), bin);
return null;
} else {
return callMethod(bin, "getAt", compiler, object, indexExp);
}
}
}
private BytecodeExpr evaluateLogicalOr(final BinaryExpression exp, CompilerTransformer compiler, Label label, boolean onTrue) {
if (onTrue) {
final BytecodeExpr l = unboxReference(exp, compiler.transformLogical(exp.getLeftExpression(), label, true), compiler);
final BytecodeExpr r = unboxReference(exp, compiler.transformLogical(exp.getRightExpression(), label, true), compiler);
return new BytecodeExpr(exp, ClassHelper.VOID_TYPE) {
protected void compile(MethodVisitor mv) {
l.visit(mv);
r.visit(mv);
}
};
} else {
final Label _true = new Label();
final BytecodeExpr l = unboxReference(exp, compiler.transformLogical(exp.getLeftExpression(), _true, true), compiler);
final BytecodeExpr r = unboxReference(exp, compiler.transformLogical(exp.getRightExpression(), label, false), compiler);
return new BytecodeExpr(exp, ClassHelper.VOID_TYPE) {
protected void compile(MethodVisitor mv) {
l.visit(mv);
r.visit(mv);
mv.visitLabel(_true);
}
};
}
}
private BytecodeExpr evaluateLogicalAnd(final BinaryExpression exp, CompilerTransformer compiler, Label label, boolean onTrue) {
if (onTrue) {
final Label _false = new Label();
final BytecodeExpr l = unboxReference(exp, compiler.transformLogical(exp.getLeftExpression(), _false, false), compiler);
final BytecodeExpr r = unboxReference(exp, compiler.transformLogical(exp.getRightExpression(), label, true), compiler);
return new BytecodeExpr(exp, ClassHelper.VOID_TYPE) {
protected void compile(MethodVisitor mv) {
l.visit(mv);
r.visit(mv);
mv.visitLabel(_false);
}
};
} else {
final BytecodeExpr l = unboxReference(exp, compiler.transformLogical(exp.getLeftExpression(), label, false), compiler);
final BytecodeExpr r = unboxReference(exp, compiler.transformLogical(exp.getRightExpression(), label, false), compiler);
return new BytecodeExpr(exp, ClassHelper.VOID_TYPE) {
protected void compile(MethodVisitor mv) {
l.visit(mv);
r.visit(mv);
}
};
}
}
private void intCmp(int op, boolean onTrue, MethodVisitor mv, Label label) {
switch (op) {
case Types.COMPARE_NOT_EQUAL:
mv.visitJumpInsn(onTrue ? IFNE : IFEQ, label);
break;
case Types.COMPARE_EQUAL:
mv.visitJumpInsn(onTrue ? IFEQ : IFNE, label);
break;
case Types.COMPARE_LESS_THAN:
mv.visitJumpInsn(onTrue ? IFLT : IFGE, label);
break;
case Types.COMPARE_LESS_THAN_EQUAL:
mv.visitJumpInsn(onTrue ? IFLE : IFGT, label);
break;
case Types.COMPARE_GREATER_THAN:
mv.visitJumpInsn(onTrue ? IFGT : IFLE, label);
break;
case Types.COMPARE_GREATER_THAN_EQUAL:
mv.visitJumpInsn(onTrue ? IFGE : IFLT, label);
break;
default:
throw new IllegalStateException();
}
}
private BytecodeExpr evaluateCompare(final BinaryExpression be, final CompilerTransformer compiler, final Label label, final boolean onTrue, final int op) {
final Operands operands = new Operands(be, compiler);
BytecodeExpr l = compiler.transformSynthetic(operands.getLeft());
BytecodeExpr r = compiler.transformSynthetic(operands.getRight());
if (TypeUtil.isNumericalType(l.getType()) && TypeUtil.isNumericalType(r.getType())) {
final ClassNode mathType = TypeUtil.getMathType(l.getType(), r.getType());
final BytecodeExpr l1 = l;
final BytecodeExpr r1 = r;
return new BytecodeExpr(be, ClassHelper.boolean_TYPE) {
public void compile(MethodVisitor mv) {
l1.visit(mv);
box(l1.getType(), mv);
cast(TypeUtil.wrapSafely(l1.getType()), TypeUtil.wrapSafely(mathType), mv);
if (ClassHelper.isPrimitiveType(mathType))
unbox(mathType, mv);
r1.visit(mv);
box(r1.getType(), mv);
cast(TypeUtil.wrapSafely(r1.getType()), TypeUtil.wrapSafely(mathType), mv);
if (ClassHelper.isPrimitiveType(mathType))
unbox(mathType, mv);
if (mathType == ClassHelper.int_TYPE) {
switch (op) {
case Types.COMPARE_EQUAL:
mv.visitJumpInsn(onTrue ? IF_ICMPEQ : IF_ICMPNE, label);
break;
case Types.COMPARE_NOT_EQUAL:
mv.visitJumpInsn(onTrue ? IF_ICMPNE : IF_ICMPEQ, label);
break;
case Types.COMPARE_LESS_THAN:
mv.visitJumpInsn(onTrue ? IF_ICMPLT : IF_ICMPGE, label);
break;
case Types.COMPARE_LESS_THAN_EQUAL:
mv.visitJumpInsn(onTrue ? IF_ICMPLE : IF_ICMPGT, label);
break;
case Types.COMPARE_GREATER_THAN:
mv.visitJumpInsn(onTrue ? IF_ICMPGT : IF_ICMPLE, label);
break;
case Types.COMPARE_GREATER_THAN_EQUAL:
mv.visitJumpInsn(onTrue ? IF_ICMPGE : IF_ICMPLT, label);
break;
default:
throw new IllegalStateException();
}
} else if (mathType == ClassHelper.double_TYPE) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "compare", "(DD)I");
intCmp(op, onTrue, mv, label);
} else if (mathType == ClassHelper.long_TYPE) {
mv.visitInsn(LCMP);
intCmp(op, onTrue, mv, label);
} else if (mathType == ClassHelper.BigInteger_TYPE) {
mv.visitMethodInsn(INVOKEVIRTUAL, "java/math/BigInteger", "compareTo", "(Ljava/math/BigInteger;)I");
intCmp(op, onTrue, mv, label);
} else if (mathType == ClassHelper.BigDecimal_TYPE) {
mv.visitMethodInsn(INVOKEVIRTUAL, "java/math/BigDecimal", "compareTo", "(Ljava/math/BigDecimal;)I");
intCmp(op, onTrue, mv, label);
} else {
mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "compareTo", "(Ljava/lang/Number;Ljava/lang/Number;)I");
intCmp(op, onTrue, mv, label);
}
}
};
} else {
final BytecodeExpr l2 = l;
final BytecodeExpr r2 = r;
final boolean leftNull = l2.getType().equals(TypeUtil.NULL_TYPE);
final boolean rightNull = r2.getType().equals(TypeUtil.NULL_TYPE);
final int opType = be.getOperation().getType();
if ((leftNull || rightNull) && (opType == Types.COMPARE_EQUAL || opType == Types.COMPARE_NOT_EQUAL || opType == Types.COMPARE_IDENTICAL || opType == Types.COMPARE_NOT_IDENTICAL)) {
return new BytecodeExpr(be, ClassHelper.boolean_TYPE) {
public void compile(MethodVisitor mv) {
if (rightNull) {
l2.visit(mv);
box(l2.getType(), mv);
}
else {
r2.visit(mv);
box(r2.getType(), mv);
}
switch (opType) {
case Types.COMPARE_EQUAL:
case Types.COMPARE_IDENTICAL:
mv.visitJumpInsn(onTrue ? IFNULL : IFNONNULL, label);
break;
case Types.COMPARE_NOT_EQUAL:
case Types.COMPARE_NOT_IDENTICAL:
mv.visitJumpInsn(onTrue ? IFNONNULL : IFNULL, label);
break;
}
}
};
}
if (opType == Types.COMPARE_EQUAL || opType == Types.COMPARE_NOT_EQUAL) {
return evaluateEqualNotEqual(be, compiler, label, onTrue, l2, r2, opType);
}
return new BytecodeExpr(be, ClassHelper.boolean_TYPE) {
public void compile(MethodVisitor mv) {
l2.visit(mv);
box(l2.getType(), mv);
r2.visit(mv);
box(r2.getType(), mv);
switch (opType) {
case Types.COMPARE_IDENTICAL:
mv.visitJumpInsn(onTrue ? IF_ACMPEQ : IF_ACMPNE, label);
break;
case Types.COMPARE_NOT_IDENTICAL:
mv.visitJumpInsn(onTrue ? IF_ACMPNE : IF_ACMPEQ, label);
break;
case Types.COMPARE_LESS_THAN:
mv.visitMethodInsn(INVOKESTATIC, TypeUtil.DTT_INTERNAL, "compareTo", "(Ljava/lang/Object;Ljava/lang/Object;)I");
mv.visitJumpInsn(onTrue ? IFLT : IFGE, label);
break;
case Types.COMPARE_LESS_THAN_EQUAL:
mv.visitMethodInsn(INVOKESTATIC, TypeUtil.DTT_INTERNAL, "compareTo", "(Ljava/lang/Object;Ljava/lang/Object;)I");
mv.visitJumpInsn(onTrue ? IFLE : IFGT, label);
break;
case Types.COMPARE_GREATER_THAN:
mv.visitMethodInsn(INVOKESTATIC, TypeUtil.DTT_INTERNAL, "compareTo", "(Ljava/lang/Object;Ljava/lang/Object;)I");
mv.visitJumpInsn(onTrue ? IFGT : IFLE, label);
break;
case Types.COMPARE_GREATER_THAN_EQUAL:
mv.visitMethodInsn(INVOKESTATIC, TypeUtil.DTT_INTERNAL, "compareTo", "(Ljava/lang/Object;Ljava/lang/Object;)I");
mv.visitJumpInsn(onTrue ? IFGE : IFLT, label);
break;
default:
throw new UnsupportedOperationException();
}
}
};
}
}
/*
The logic below is following:
- Object.equals(Object) evaluated statically
- Object.equals(T) evaluated as T.equals(Object)
- equals selection takes care for extension methods
*/
private BytecodeExpr evaluateEqualNotEqual(final BinaryExpression be, CompilerTransformer compiler, final Label label, final boolean onTrue, final BytecodeExpr left, final BytecodeExpr right, final int opType) {
if (left.getType().equals(ClassHelper.OBJECT_TYPE)) {
if (right.getType().equals(ClassHelper.OBJECT_TYPE)) {
return new BytecodeExpr(be, ClassHelper.boolean_TYPE) {
public void compile(MethodVisitor mv) {
left.visit(mv);
box(left.getType(), mv);
right.visit(mv);
box(right.getType(), mv);
switch (opType) {
case Types.COMPARE_EQUAL:
mv.visitMethodInsn(INVOKESTATIC, "org/mbte/groovypp/runtime/DefaultGroovyPPMethods", "equals", "(Ljava/lang/Object;Ljava/lang/Object;)Z");
mv.visitJumpInsn(onTrue ? IFNE : IFEQ, label);
break;
case Types.COMPARE_NOT_EQUAL:
mv.visitMethodInsn(INVOKESTATIC, "org/mbte/groovypp/runtime/DefaultGroovyPPMethods", "equals", "(Ljava/lang/Object;Ljava/lang/Object;)Z");
mv.visitJumpInsn(onTrue ? IFEQ : IFNE, label);
break;
}
}
};
}
else {
return evaluateEqualNotEqual(be, compiler, label, onTrue, right, left, opType);
}
}
final ClassNode wrapper = ClassHelper.getWrapper(left.getType());
if (wrapper.implementsInterface(TypeUtil.COMPARABLE) || wrapper.equals(TypeUtil.COMPARABLE)) {
return new BytecodeExpr(be, ClassHelper.boolean_TYPE) {
public void compile(MethodVisitor mv) {
left.visit(mv);
box(left.getType(), mv);
right.visit(mv);
box(right.getType(), mv);
switch (opType) {
case Types.COMPARE_EQUAL:
mv.visitMethodInsn(INVOKESTATIC, "org/mbte/groovypp/runtime/DefaultGroovyPPMethods", "compareToWithEqualityCheck", "(Ljava/lang/Object;Ljava/lang/Object;)I");
mv.visitJumpInsn(onTrue ? IFEQ : IFNE, label);
break;
case Types.COMPARE_NOT_EQUAL:
mv.visitMethodInsn(INVOKESTATIC, "org/mbte/groovypp/runtime/DefaultGroovyPPMethods", "compareToWithEqualityCheck", "(Ljava/lang/Object;Ljava/lang/Object;)I");
mv.visitJumpInsn(onTrue ? IFNE : IFEQ, label);
break;
}
}
};
}
else {
final MethodCallExpression mce = new MethodCallExpression(left, "equals", right);
mce.setSourcePosition(be);
return ExprTransformer.transformLogicalExpression(mce, compiler, label, opType == Types.COMPARE_EQUAL ? onTrue : !onTrue);
}
}
private static BytecodeExpr evaluateFindRegexp(final BinaryExpression exp, final CompilerTransformer compiler) {
final BytecodeExpr left = (BytecodeExpr) compiler.transform(exp.getLeftExpression());
final BytecodeExpr right = (BytecodeExpr) compiler.transform(exp.getRightExpression());
return new BytecodeExpr(exp, TypeUtil.MATCHER) {
protected void compile(MethodVisitor mv) {
left.visit(mv);
if (ClassHelper.isPrimitiveType(left.getType()))
box(left.getType(), mv);
right.visit(mv);
if (ClassHelper.isPrimitiveType(right.getType()))
box(right.getType(), mv);
mv.visitMethodInsn(
INVOKESTATIC, "org/codehaus/groovy/runtime/InvokerHelper",
"findRegex", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/regex/Matcher;");
}
};
}
private static BytecodeExpr evaluateMatchRegexp(final BinaryExpression exp, final CompilerTransformer compiler) {
final BytecodeExpr left = (BytecodeExpr) compiler.transform(exp.getLeftExpression());
final BytecodeExpr right = (BytecodeExpr) compiler.transform(exp.getRightExpression());
return new BytecodeExpr(exp, ClassHelper.boolean_TYPE) {
protected void compile(MethodVisitor mv) {
left.visit(mv);
if (ClassHelper.isPrimitiveType(left.getType()))
box(left.getType(), mv);
right.visit(mv);
if (ClassHelper.isPrimitiveType(right.getType()))
box(right.getType(), mv);
mv.visitMethodInsn(
INVOKESTATIC, "org/codehaus/groovy/runtime/InvokerHelper",
"matchRegex", "(Ljava/lang/Object;Ljava/lang/Object;)Z");
}
};
}
private static class Logical extends BytecodeExpr {
private final Label _false = new Label(), _end = new Label();
private final BytecodeExpr be;
public Logical(Expression parent, CompilerTransformer compiler) {
super(parent, ClassHelper.boolean_TYPE);
be = compiler.transformLogical(parent, _false, false);
}
protected void compile(MethodVisitor mv) {
be.visit(mv);
mv.visitInsn(ICONST_1);
mv.visitJumpInsn(GOTO, _end);
mv.visitLabel(_false);
mv.visitInsn(ICONST_0);
mv.visitLabel(_end);
}
}
private class Operands {
private BytecodeExpr left;
private BytecodeExpr right;
public Operands(BinaryExpression be, CompilerTransformer compiler) {
left = (BytecodeExpr) compiler.transform(be.getLeftExpression());
right = (BytecodeExpr) compiler.transform(be.getRightExpression());
if (!TypeUtil.areTypesDirectlyConvertible(left.getType(), right.getType())) {
left = unboxReference(be, left, compiler);
right = unboxReference(be, right, compiler);
}
}
public BytecodeExpr getLeft() {
return left;
}
public BytecodeExpr getRight() {
return right;
}
}
}